package org.example.mallchat.service.impl;

import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.thread.NamedThreadFactory;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.example.mallchat.common.thread.GlobalUncaughtExceptionHandler;
import org.example.mallchat.dao.UserDao;
import org.example.mallchat.domain.entity.IpDetail;
import org.example.mallchat.domain.entity.IpInfo;
import org.example.mallchat.domain.entity.IpResult;
import org.example.mallchat.domain.entity.User;
import org.example.mallchat.service.IpService;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author: zzh
 * @date: 2024/12/24 16:21:53
 * @version: 1.0
 */
// 实现接口DisposableBean，用于在销毁时释放资源 优雅停机
@Service
@Slf4j
public class IpServiceImpl implements IpService , DisposableBean {
    /**
     * 线程池
     * 线程池的大小为1，队列大小为500，线程空闲时间为0，线程工厂为NamedThreadFactory，异常处理为GlobalUncaughtExceptionHandler
     * 保证排队处理，和淘宝接口同步！频控！
     */
    private static final ExecutorService EXECUTOR = new ThreadPoolExecutor(1, 1,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(500),
            new NamedThreadFactory("refresh-ipDetail", null, false,
                    GlobalUncaughtExceptionHandler.getInstance()));

    @Resource
    private UserDao userDao;


    @Override
    public void refreshIpDetailAsync(Long uid) {
        EXECUTOR.execute(() -> {
            // 查询用户
            User user = userDao.getById(uid);
            IpInfo ipInfo = user.getIpInfo();
            if (Objects.isNull(ipInfo)) {
                return;
            }
            // 更新用户ip详情
            String ip = ipInfo.needRefreshIp();
            if (Objects.isNull(ip)) {
                return;// 无需刷新
            }
            // 刷新ip详情 尝试3次获取
            IpDetail ipDetail = TryGetIpDetailOrNullTreeTimes(ip);
            if (Objects.nonNull(ipDetail)) {// 更新数据库的ip详情
                ipInfo.refreshIpDetail(ipDetail);// 其实就是刷新ip详细信息然后最后入库！
                User update = new User();
                update.setId(uid);
                update.setIpInfo(ipInfo);
                userDao.updateById(update);
            } else {
                log.error("get ip detail fail ip:{},uid:{}", ip, uid);
            }
        });
    }

    /**
     * 获取ip详细信息，失败返回null
     * 尝试3次获取，每次间隔2秒
     * @param ip
     * @return
     */
    private static IpDetail TryGetIpDetailOrNullTreeTimes(String ip) {
        for (int i = 0; i < 3; i++) {
            IpDetail ipDetail = getIpDetailOrNull(ip);
            if (Objects.nonNull(ipDetail)) {
                return ipDetail;
            }
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 获取ip详细信息，失败返回null
     * 发送接口请求，获取ip详细信息
     * @param ip
     * @return
     */
    private static IpDetail getIpDetailOrNull(String ip) {
        String body = HttpUtil.get("https://ip.taobao.com/outGetIpInfo?ip=" + ip + "&accessKey=alibaba-inc");
        // 解析json 转换为指定的对象
        try {
            IpResult<IpDetail> result = JSONUtil.toBean(body, new TypeReference<IpResult<IpDetail>>() {
            }, false);
            if (result.isSuccess()) {
                return result.getData();
            }
        } catch (Exception ignored) {
        }
        return null;
    }

    @Override
    public void destroy() throws InterruptedException {
        EXECUTOR.shutdown();
        if (!EXECUTOR.awaitTermination(30, TimeUnit.SECONDS)) {//最多等30秒，处理不完就拉倒
            if (log.isErrorEnabled()) {
                log.error("Timed out while waiting for executor [{}] to terminate", EXECUTOR);
            }
        }
    }



    //测试耗时结果 100次查询总耗时约100s，平均一次成功查询需要1s,可以接受
    //第99次成功,目前耗时：99545ms
    public static void main(String[] args) {
        Date begin = new Date();
        for (int i = 0; i < 100; i++) {
            int finalI = i;
            EXECUTOR.execute(() -> {
                IpDetail ipDetail = TryGetIpDetailOrNullTreeTimes("113.90.36.126");
                if (Objects.nonNull(ipDetail)) {
                    Date date = new Date();
                    System.out.println(String.format("第%d次成功,目前耗时：%dms", finalI, (date.getTime() - begin.getTime())));
                }
            });
        }
    }
}
